import { ESGeoExtrudedPolygon, ESGeoLineString, getMinMaxCorner } from "earthsdk3";
import { CzmESGeoPolygon } from "../CzmESGeoPolygon";
import { ESCesiumViewer } from "../../../ESCesiumViewer";
import * as Cesium from 'cesium';
import { positionsToUniqueCartesians } from "../../../utils";
import { bind, createNextAnimateFrameEvent, ObjResettingWithEvent } from "xbsj-base";

export class CzmESGeoExtrudedPolygon<T extends ESGeoExtrudedPolygon = ESGeoExtrudedPolygon> extends CzmESGeoPolygon<T> {
    static override readonly type = this.register<ESGeoExtrudedPolygon, ESCesiumViewer>("ESCesiumViewer", ESGeoExtrudedPolygon.type, this);

    public czmEntity?: Cesium.Entity
    public czmPolyline = this.ad(new ESGeoLineString())

    constructor(sceneObject: T, czmViewer: ESCesiumViewer) {
        super(sceneObject, czmViewer);
        const viewer = czmViewer.viewer;
        if (!viewer) {
            console.warn(`viewer is undefined!`);
            return;
        }
        const czmPolyline = this.czmPolyline;
        czmViewer.add(czmPolyline);
        this.ad(() => czmViewer.delete(czmPolyline));
        {
            this.ad(bind([czmPolyline, 'show'], [sceneObject, 'show']));
            this.ad(bind([czmPolyline, 'allowPicking'], [sceneObject, 'allowPicking']));
            this.ad(bind([czmPolyline, 'stroked'], [sceneObject, 'stroked']));
            this.ad(bind([czmPolyline, 'strokeColor'], [sceneObject, 'strokeColor']));
            this.ad(bind([czmPolyline, 'strokeWidth'], [sceneObject, 'strokeWidth']));
            this.ad(bind([czmPolyline, 'strokeGround'], [sceneObject, 'strokeGround']));
        }
        {
            const update = () => {
                if (!sceneObject.points || sceneObject.points.length < 2) {
                    czmPolyline.points = sceneObject.points;
                } else {
                    const points = structuredClone(sceneObject.points);
                    points.push(points[0]);
                    if (!sceneObject.perPositionHeight) {
                        czmPolyline.points = points.map(item => {
                            item[2] = sceneObject.height ?? ESGeoExtrudedPolygon.defaults.height;
                            return item
                        });
                    } else {
                        czmPolyline.points = points;
                    }
                }

            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(
                sceneObject.pointsChanged,
                sceneObject.heightChanged,
                sceneObject.perPositionHeightChanged
            ))
            this.ad(event.don(update));
        }
        const entity = this.czmEntity = viewer.entities.add({ polygon: {} });
        //@ts-ignore
        Cesium.Entity.prototype && (entity.ESSceneObjectID = sceneObject.id);
        this.dispose(() => viewer.entities.remove(entity));
        // 动态绘制
        let hierarchy: Cesium.PolygonHierarchy = new Cesium.PolygonHierarchy();
        if (entity.polygon) {
            entity.polygon.hierarchy = new Cesium.CallbackProperty(() => {
                return hierarchy;
            }, false); //使用回调函数,防止闪烁。
        }
        const updatePosition = () => {
            if (!sceneObject.points) {
                return;
            }
            const cartesians = positionsToUniqueCartesians(sceneObject.points);
            if (cartesians.length < 2) {
                hierarchy = new Cesium.PolygonHierarchy();
                return;
            }
            hierarchy = new Cesium.PolygonHierarchy(cartesians)
        }
        {
            updatePosition();
            this.dispose(sceneObject.pointsChanged.disposableOn(updatePosition));
        }
        {
            const update = () => {
                entity.show = sceneObject.show && sceneObject.filled;
                // entity.show = sceneObject.show && !sceneObject.editing && sceneObject.filled;
                if (this.geoPolygon)
                    this.geoPolygon.show = sceneObject.show && !entity.show && sceneObject.filled;
            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(
                // sceneObject.editingChanged,
                sceneObject.showChanged,
                sceneObject.filledChanged
            ))
            this.ad(event.don(update))
        }
        {
            const update = () => {
                if (entity.polygon) {
                    entity.polygon.perPositionHeight = new Cesium.ConstantProperty(sceneObject.perPositionHeight);
                    updatePosition();
                }
            }
            update();
            this.ad(sceneObject.perPositionHeightChanged.don(update));
        }
        {
            const updateProp = () => {
                if (entity.polygon) {
                    entity.polygon.height = new Cesium.ConstantProperty(sceneObject.height ?? ESGeoExtrudedPolygon.defaults.height);
                }
            }
            updateProp();
            this.dispose(sceneObject.heightChanged.disposableOn(updateProp));
        }
        {
            const updateProp = () => {
                if (entity.polygon) {
                    entity.polygon.extrudedHeight = new Cesium.ConstantProperty(sceneObject.extrudedHeight ?? ESGeoExtrudedPolygon.defaults.extrudedHeight);
                }
            }
            updateProp();
            this.dispose(sceneObject.extrudedHeightChanged.disposableOn(updateProp));
        }
        {
            const updateProp = () => {
                const ColorMaterial = Cesium.Color.fromCartesian4(
                    Cesium.Cartesian4.fromArray(sceneObject.fillColor)
                )
                if (entity.polygon)
                    //@ts-ignore
                    entity.polygon.material = ColorMaterial;
            }
            updateProp();
            this.dispose(sceneObject.fillColorChanged.disposableOn(updateProp));
        }
        const objResetting = this.disposeVar(new ObjResettingWithEvent(sceneObject.allowPickingChanged, () => {
            if (sceneObject.allowPicking) return undefined;
            return new ESCesiumViewer.ObjectsToExcludeWrapper(czmViewer, entity);
        }));
    }
    public getMinAndMaxheight() {
        const { sceneObject } = this;
        if (sceneObject.points) {
            const { minPos, maxPos } = getMinMaxCorner(sceneObject.points);
            return [minPos[2], maxPos[2]]
        }
        return [0, 0]
    }
}
